﻿/**@author W意波编写*/
#ifndef LK_MOBJECT_
#define LK_MOBJECT_
namespace LK
{
	/**
	@brief 表示一个封箱接口*/
	struct MObjects :ICloneables
	{
		/**
		@brief 获取对象类型，这里比较特别：封箱对象总是返回Type::Make<MObjects>()*/
		ptr<Type> GetType()const;
		/**
		@breif 产生一个副本*/
		MObjects* Clone()const;
		/**
		@brief 判断是否存储了独立的副本*/
		bool IsCopy()const;
		/**
		@brief 获取内部保存的数据的指针，该方法用以直接修改封箱内实际存储的数据*/
		virtual void*GetDataPtr() { return NULL; }
		/**
		@brief 获取所表示对象的地址*/
		virtual void* GetObjectPtr();
		/**
		@brief 获取一个ptr所表示对象的地址，特别的，如果obj不指向封箱对象将返回NULL*/
		static void* GetObjectPtr(PObject const& obj);
		/**
		@brief 获取表示对象或引用所用的数据的地址，特别的，如果obj不指向封箱对象将返回其本身的地址*/
		static void* GetDataPtr(PObject const& obj);
		/**
		@brief 制作一个指定类型的PObject对象*/
		template<typename T>inline static PObject MakeObject(T t);
		/**
		@brief 获取封箱表示的数据类型*/
		ptr<Type> GetInnerType()const;
		/**
		@brif 添加类型信息*/
		static void AddTypeInfo();
		/**
		@brief 通过指定的类型信息和该信息封箱实际存储数据的指针来构造一个封箱对象
		@note 如果pdat为NULL，则使用默认构造方法进行构造*/
		static PMObjects MakeMObjectByTypeInfo(ptr<Type> const&, const void*pdat = NULL);
		/**
		@brief 通过指定的对象指针和类型信息和来构造一个封箱对象
		@note 如果pobj为NULL将抛出PPointerIsNullException 异常
		@note 如果指定的type为左值引用，则会生成pobj所指向对象的副本*/
		static PMObjects MakeMObjectByTypeInfo(const void*pobj, ptr<Type> const&);
	protected:
		/**
		@brief 内部数据类型*/
		ptr<Object> innerDataType;
		friend struct ptr<MObjects>;
	};
	/**
	@brief 表示一个封箱存储实体*/
	template<size_t size>struct MObject :MObjects {
		void* ptmp;//临时内容
		union {
			void* ptr;
			unsigned char data[size];//内容占用
		};
		MObject() :ptmp(NULL) {}
		void*GetDataPtr() { return data; }
		~MObject();
	};
	template<>struct MObject <0> :MObjects{ void*GetDataPtr() { return NULL; } };//空封箱对象
	/**
	@brief 获取封箱数据
	@note 如果获取失败将抛出异常*/
	template <class T>T cast(PMObjects const&m);

}
#endif